package com.maxlen.entity.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.maxlen.common.utils.R;
import com.maxlen.constant.RedisConstant;
import com.maxlen.entity.service.SeckillService;
import com.maxlen.entity.to.SeckillSessionTO;
import com.maxlen.feign.CouponFeignService;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void uploadSeckillSkuLatest3Days() {
        R skuWithLately3Day = couponFeignService.getSkuWithLately3Day();
        if(skuWithLately3Day.isOk()){
            List<SeckillSessionTO> seckillSessionTOList = skuWithLately3Day.getData(new TypeReference<List<SeckillSessionTO>>(){});
            seckillSessionTOList.forEach(seckillSessionTO -> {
                String key = RedisConstant.SESSION_CACHE_PREFIX + seckillSessionTO.getStartTime().getTime() + "_" + seckillSessionTO.getEndTime().getTime();

                // 解决幂等性问题
                if(Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
                    List<String> stringList = seckillSessionTO.getSeckillSkuRelationTO().stream().map((item) -> {
                        return seckillSessionTO.getId() + "_" + item.getSkuId().toString();
                    }).collect(Collectors.toList());
                    stringRedisTemplate.opsForList().leftPushAll(key, stringList);

                    BoundHashOperations<String, Object, Object> boundHashOps = stringRedisTemplate.boundHashOps(RedisConstant.SKU_CACHE_PREFIX);
                    seckillSessionTO.getSeckillSkuRelationTO().forEach(seckillSkuRelationTO -> {

                        // 设置商品的秒杀时间信息
                        seckillSkuRelationTO.setStartTime(seckillSessionTO.getStartTime().getTime());
                        seckillSkuRelationTO.setEndTime(seckillSessionTO.getEndTime().getTime());

                        // 设置随机码
                        String token = UUID.randomUUID().toString().replace("-", "");
                        seckillSkuRelationTO.setRandomCode(token);

                        String toJSONString = JSON.toJSONString(seckillSkuRelationTO);
                        boundHashOps.put(seckillSessionTO.getId() + "_" + seckillSkuRelationTO.getSkuId().toString(), toJSONString);

                        // 解决幂等性问题
                        String semaphoreKey = RedisConstant.SKU_STOCK_SEMAPHORE + token;
                        if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(semaphoreKey))) {
                            // redis 信号量
                            // 使用库存作为分布式的信号量 限流
                            RSemaphore semaphore = redissonClient.getSemaphore(semaphoreKey);
                            semaphore.trySetPermits(seckillSkuRelationTO.getSeckillCount().intValue());
                        }
                    });
                }
            });
        }
    }

    @Override
    public List<SeckillSessionTO.SeckillSkuRelationTO> getCurrentSeckillSkuList() {

        Set<String> keys = new HashSet<>();

        // 通过 scan 遍历 redis 所有 key
        stringRedisTemplate.execute((RedisConnection connection) -> {
            Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(RedisConstant.SESSION_CACHE_PREFIX + "*").count(Integer.MAX_VALUE).build());
            cursor.forEachRemaining(bytes -> {
                keys.add(new String(bytes));
            });
            return null;
        });

        long time = new Date().getTime();

        for (String key : keys) {
            String replace = key.replace(RedisConstant.SESSION_CACHE_PREFIX, "");
            String[] split = replace.split("_");
            long start = Long.parseLong(split[0]);
            long end = Long.parseLong(split[1]);
            System.out.println(start + " - " + time + " - " + end);
            if(time >= start && time <= end){
                List<String> stringList = stringRedisTemplate.opsForList().range(key, 0, -1);
                BoundHashOperations<String, String, Object> operations = stringRedisTemplate.boundHashOps(RedisConstant.SKU_CACHE_PREFIX);

                if(stringList == null){
                    return null;
                }

                List<Object> list = operations.multiGet(stringList);
                System.out.println(list);
                if(list != null){
                    return list.stream().map(item -> {
                        return JSON.parseObject(item.toString(), SeckillSessionTO.SeckillSkuRelationTO.class);
                    }).collect(Collectors.toList());
                }
                break;
            }
        }

       return null;
    }

    @Override
    public List<SeckillSessionTO.SeckillSkuRelationTO> getSkuSeckillInfo(Long skuId) {
        List<SeckillSessionTO.SeckillSkuRelationTO> seckillSkuRelationTOList = new ArrayList<>();
        BoundHashOperations<String, Object, Object> operations =
                stringRedisTemplate.boundHashOps(RedisConstant.SKU_CACHE_PREFIX);
        Cursor<Map.Entry<Object, Object>> cursor = operations.scan(ScanOptions.scanOptions().match("*_" + skuId).count(Long.MAX_VALUE).build());
        cursor.forEachRemaining(objectObjectEntry -> {
            SeckillSessionTO.SeckillSkuRelationTO seckillSkuRelationTO = JSON.parseObject(objectObjectEntry.getValue().toString(), SeckillSessionTO.SeckillSkuRelationTO.class);
            long time = new Date().getTime();
            if(!(time >= seckillSkuRelationTO.getStartTime() && time <= seckillSkuRelationTO.getEndTime())){
                seckillSkuRelationTO.setRandomCode(null);
            }
            seckillSkuRelationTOList.add(seckillSkuRelationTO);
        });
        return seckillSkuRelationTOList;
    }

}
